﻿using System;
using System.Collections.Generic;
using System.Linq;
using NDBGen.Descriptors;
using NDBGen.Descriptors.Columns.DataTypes;
using NDBGen.Descriptors.Columns.Tokens;
using NDBGen.Descriptors.Tables.Tokens;

namespace NDBGen.Plugins
{
    public class PluginData<TColumnDataTypeSnippetGenerator, TColumnTokenSnippetGenerator, TTableTokenSnippetGenerator> : IPluginData
        where TColumnDataTypeSnippetGenerator : IGenerator<IColumnDataTypeDescriptor>
        where TColumnTokenSnippetGenerator : IGenerator<IColumnTokenDescriptor>
        where TTableTokenSnippetGenerator : IGenerator<ITableTokenDescriptor>
    {
        private static readonly Func<IGeneratorMetadata, Type> keySelector = (x => x.GeneratorForDescriptorType);

        private Func<IGeneratorMetadata, Boolean> GetFilterPredicate(IEnumerable<Type> types)
        {
            return (x => types.Contains(x.GeneratorForDescriptorType));
        }

        internal PluginData(ParsedInput parsedInput)
        {
            this.ParsedInput = parsedInput;
            var libraries = new GeneratorLibraries<TColumnDataTypeSnippetGenerator, TColumnTokenSnippetGenerator, TTableTokenSnippetGenerator>();

            var columnDataTypeDescriptorTypes = (
                from table in parsedInput.Tables
                from column in table.Columns
                select column.DataTypeDescriptor.GetType()).Distinct();

            var columnTokenDescriptorTypes = (
                from table in parsedInput.Tables
                from column in table.Columns
                from tokenDescriptor in column.Descriptors
                select tokenDescriptor.GetType()).Distinct();

            var tableTokenDescriptorTypes = (
                from table in parsedInput.Tables
                from tokenDescriptor in table.Descriptors
                select tokenDescriptor.GetType()).Distinct();

            this.ColumnDataTypeGenerators = libraries.ColumnDataTypeGenerators.FindTypeDictionary(GetFilterPredicate(columnDataTypeDescriptorTypes), keySelector);
            this.ColumnTokenGenerators = libraries.ColumnTokenGenerators.FindTypeDictionary(GetFilterPredicate(columnTokenDescriptorTypes), keySelector);
            this.TableTokenGenerators = libraries.TableTokenGenerators.FindTypeDictionary(GetFilterPredicate(tableTokenDescriptorTypes), keySelector);
        }

        public ParsedInput ParsedInput { get; protected set; }

        internal ReadOnlyDictionary<Type, Type> ColumnDataTypeGenerators { get; private set; }
        internal ReadOnlyDictionary<Type, Type> ColumnTokenGenerators { get; private set; }
        internal ReadOnlyDictionary<Type, Type> TableTokenGenerators { get; private set; }

        #region [ IPluginData Members ]

        ReadOnlyDictionary<Type, Type> IPluginData.ColumnDataTypeGenerators
        {
            get { return this.ColumnDataTypeGenerators; }
        }

        ReadOnlyDictionary<Type, Type> IPluginData.ColumnTokenGenerators
        {
            get { return this.ColumnTokenGenerators; }
        }

        ReadOnlyDictionary<Type, Type> IPluginData.TableTokenGenerators
        {
            get { return this.TableTokenGenerators; }
        }

        #endregion
    }
}